﻿// 源文件头信息：
// <copyright file="RepositoryContextBase.cs">
// Copyright(c)2012-2019.
// CLR版本：4.0.30319.239
// 开发组织：luochengbang@163.com
// 公司网站：
// 所属工程：RH.EBS.Core
// 最后修改：Ben.Luo
// 最后修改：2013/05/27 18:10
// </copyright>

using RH.EBS.BaseEntitys;
using RH.EBS.Core.Repository.Tools;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Core;
using System.Data.Entity.Core.Objects;
using System.Data.Entity.Infrastructure;
using System.Data.SqlClient;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using RH.EBS.Core.Repository.Tools.Extensions;
using System.Data.Common;
using System.Runtime.CompilerServices;
using System.Transactions;

namespace RH.EBS.Core.Repository
{
    /// <summary>
    ///     单元操作实现基类
    /// </summary>
    public abstract class BaseContext<TEntity> : DbContext where TEntity : Entity
    {
        private static object lockobj = new object();
        private static object lockUpdater = new object();
        /// <summary>
        /// 获取 当前使用的数据访问上下文对象
        /// </summary>        
        protected DbContext Context = null;

        protected ObjectContext ObjContext = null;

        /// <summary>
        ///     获取 当前实体的查询数据集
        /// </summary>
        public virtual DbQuery<TEntity> Entities
        {
            get { return this.Set(); }
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public BaseContext(DbContext context)
        {
            this.Context = context;
            ObjContext = ((IObjectContextAdapter)context).ObjectContext;
        }

        /// <summary>
        /// 重写保存方法
        /// </summary>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.Synchronized)]
        public override int SaveChanges()
        {
            //using (ObjContext)
            {
                int result = 0;
                try
                {
                    ObjContext.CommandTimeout = 5 * 60;
                    result += SaveDbComnds();
                    result = ObjContext.SaveChanges();
                }
                catch (OptimisticConcurrencyException ex)
                {
                    Loger.WriteLog(ex);
                    try
                    {
                        //刷新实体重新提交
                        if (ex.StateEntries != null && ex.StateEntries.Count > 0)
                        {
                            foreach (var entry in ex.StateEntries)
                            {
                                ObjContext.Refresh(RefreshMode.StoreWins, entry.Entity);
                            }
                            ObjContext.SaveChanges();
                        }
                    }
                    catch (OptimisticConcurrencyException ex2)
                    {
                        foreach (var entry in ex2.StateEntries)
                        {
                            var entity = entry.Entity;
                            if (ObjContext.ObjectStateManager.GetObjectStateEntry(entity).State == EntityState.Deleted
                                || ObjContext.ObjectStateManager.GetObjectStateEntry(entity).State == EntityState.Modified)
                                ObjContext.Refresh(RefreshMode.StoreWins, entity);
                            else if (ObjContext.ObjectStateManager.GetObjectStateEntry(entity).State == EntityState.Added)
                                ObjContext.Detach(entity);
                        }
                        ObjContext.AcceptAllChanges();
                    }
                }
                catch (DbUpdateException e)
                {
                    if (e.InnerException != null && e.InnerException.InnerException is SqlException)
                    {
                        SqlException sqlEx = e.InnerException.InnerException as SqlException;
                        string msg = DataHelper.GetSqlExceptionMessage(sqlEx.Number);
                        throw PublicHelper.ThrowDataAccessException("提交数据更新时发生异常：" + msg, sqlEx);
                    }
                    throw;
                }
                catch (Exception ex)
                {
                    ExceptionHand(ex);
                    throw;
                }
                finally
                {
                    Context.Configuration.AutoDetectChangesEnabled = true;
                    Context.Configuration.ValidateOnSaveEnabled = true;
                }
                return result;
            }

        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public int SaveDbComnds()
        {
            int result = 0;
            var dbComnds = this.Context.GetDbComnds();
            if (dbComnds != null && dbComnds.Count > 0)
            {
                try
                {
                    using (var trans = new TransactionScope())
                    {
                        try
                        {
                            foreach (var item in dbComnds)
                            {
                                result += ObjContext.ExecuteStoreCommand(item.Sql, item.Params);
                            }
                            trans.Complete();
                        }
                        catch (Exception ex)
                        {
                            throw ex;
                        }
                        finally
                        {
                            trans.Dispose();
                        }
                    }
                }
                catch (Exception ex)
                {
                    throw ex;
                }
                this.Context.ClearAll();
            }
            return result;
        }

        #region Query
        /// <summary>
        ///   为指定的类型返回 System.Data.Entity.DbSet，这将允许对上下文中的给定实体执行 CRUD 操作。
        /// </summary>
        /// <typeparam name="TEntity"> 应为其返回一个集的实体类型。 </typeparam>
        /// <returns> 给定实体类型的 System.Data.Entity.DbSet 实例。 </returns>
        public virtual DbQuery<TEntity> Set(bool isReload = false)
        {
            return isReload ? Context.Set<TEntity>().AsNoTracking() : Context.Set<TEntity>();
        }

        public virtual DbQuery<T> Set<T>(bool isReload = false)
            where T : class
        {
            return isReload ? Context.Set<T>().AsNoTracking() : Context.Set<T>();
        }
        /// <summary>
        /// 不分页获取所有数据
        /// </summary>
        /// <returns></returns>
        public virtual List<TEntity> GetList(bool isReload = false)
        {
            return this.Set(isReload).ToList();
        }

        /// <summary>
        /// 根据主键获取单个对象
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.Synchronized)]
        public virtual TEntity GetByID(params object[] keyValues)
        {
            //lock (lockobj)
            {
                PublicHelper.CheckArgument(keyValues, "keyValues");
                bool isReload = false;
                if (keyValues.Length > 0)
                {
                    Boolean.TryParse(keyValues.FirstOrDefault().ToString(), out isReload);
                }
                var keyValuesthen = new List<object>();
                keyValuesthen = keyValues.ToList();
                if (isReload)
                {
                    keyValuesthen.RemoveAt(0);
                }
                var result = Context.Set<TEntity>().Find(keyValuesthen.ToArray());
                if (result != null && isReload)
                {
                    Context.Entry(result).Reload();
                }
                return result;
            }
        }

        /// <summary>
        /// 根据条件查询，返回一个实体
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="condition"></param>
        /// <returns></returns>
        public virtual TEntity GetBy<TEntity>(Expression<Func<TEntity, bool>> condition, bool isReload = false) where TEntity : Entity
        {
            PublicHelper.CheckArgument(condition, "condition");
            TEntity entity = default(TEntity);

            //entity = Context.Set<TEntity>().Where(condition).AsNoTracking().FirstOrDefault();
            var query = Context.Set<TEntity>().Where(condition);
            entity = isReload ? query.AsNoTracking().FirstOrDefault() : query.FirstOrDefault();
            return entity;
        }

        /// <summary>
        /// 根据固定条件查询，返回一个实体集合
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="condition"></param>
        /// <returns></returns>
        public virtual List<T> GetList<T, TOrderBy>(Expression<Func<T, bool>> condition, Expression<Func<T, TOrderBy>> orderby = null, bool isReload = false)
            where T : Entity
        {
            PublicHelper.CheckArgument(condition, "condition");
            var query = Context.Set<T>().Where(condition);
            query = isReload ? query.AsNoTracking() : query;

            if (orderby != null)
            {
                return new List<T>(query.OrderBy(orderby));
            }
            else
            {
                return new List<T>(query);
            }
        }

        public virtual List<T> GetList<T, TOrderBy>(Expression<Func<T, bool>> condition, Dictionary<int, Expression<Func<T, TOrderBy>>> orderbys = null, bool isReload = false)
            where T : Entity
        {

            PublicHelper.CheckArgument(condition, "condition");
            var query = Context.Set<T>().Where(condition);
            query = isReload ? query.AsNoTracking() : query;

            int count = orderbys.Count;
            if (orderbys != null && count > 0)
            {
                int i = 0;
                IOrderedQueryable<T> queryOrder = query.OrderBy(c => 1 == 1);
                foreach (KeyValuePair<int, Expression<Func<T, TOrderBy>>> item in orderbys)
                {
                    if (item.Key == 0)
                    {
                        if (i == 0)
                        {
                            queryOrder = query.OrderBy(item.Value);
                        }
                        else
                        {
                            queryOrder = queryOrder.ThenBy(item.Value);
                        }
                    }
                    else
                    {
                        if (i == 0)
                        {
                            queryOrder = query.OrderByDescending(item.Value);
                        }
                        else
                        {
                            queryOrder = queryOrder.ThenByDescending(item.Value);
                        }
                    }
                }
                return queryOrder.ToList();
            }
            else
            {
                return new List<T>(query);
            }
        }
        public virtual IQueryable<T> Paging<T>(IOrderedQueryable<T> iQueryable, PaginationDTO paginatonDTO, bool isReload = false)
           where T : class
        {
            IQueryable<T> query = null;
            PublicHelper.CheckArgument(iQueryable, "iQueryable");
            PublicHelper.CheckArgument(paginatonDTO, "paginatonDTO");

            if (paginatonDTO.ReCount)
            {
                paginatonDTO.RowCount = iQueryable.Count();
            }

            if (paginatonDTO.IsPaging)
            {
                paginatonDTO.CurrentIndex = paginatonDTO.CurrentIndex < 1 ? 1 : paginatonDTO.CurrentIndex;
                query = iQueryable.Skip(paginatonDTO.PageSize * (paginatonDTO.CurrentIndex - 1)).Take(paginatonDTO.PageSize);
            }
            else
            { query = iQueryable; }

            return isReload ? query.AsNoTracking() : query;
        }


        /// <summary>
        /// 根据集合为某列合生成orelse连接条件 
        /// </summary>
        /// <typeparam name="TSource">集合项类型</typeparam>
        /// <param name="sourcePropertyName">数据库字段名</param>
        /// <param name="methodName">方法名称（IndexOf）</param>
        /// <param name="objs">条件数据集合</param>
        /// <returns></returns>
        public virtual Expression<Func<TSource, bool>> GenerateOrElseConditionWithArray<TSource>(string sourcePropertyName, string methodName, IEnumerable<object> objs)
        {
            PublicHelper.CheckArgument(objs, "objs");
            PublicHelper.CheckArgument(sourcePropertyName, "sourcePropertyName");
            PublicHelper.CheckArgument(methodName, "methodName");
            if (objs != null && objs.Count() > 0)
            {
                var len = objs.Count();
                var p = Expression.Parameter(typeof(TSource), "p");
                var propertyName = Expression.Property(p, sourcePropertyName);
                var body = Expression.GreaterThanOrEqual(Expression.Call(propertyName, methodName, null, Expression.Constant(objs.First())), Expression.Constant(0, typeof(int)));
                for (int i = 1; i < len; i++)
                {
                    var pcode = objs.ElementAt(i);
                    body = Expression.OrElse(body, Expression.GreaterThanOrEqual(Expression.Call(propertyName, methodName, null, Expression.Constant(pcode)), Expression.Constant(0, typeof(int))));//Expression.Call(propertyName, methodName, null, Expression.Constant(pcode)));
                }
                Expression<Func<TSource, bool>> orExp = Expression.Lambda<Func<TSource, bool>>(body, p);
                return orExp;
            }
            return null;
        }

        #endregion

        #region ADD
        /// <summary>
        ///     注册一个新的对象到仓储上下文中
        /// </summary>
        /// <typeparam name="TEntity"> 要注册的类型 </typeparam>
        /// <param name="entity"> 要注册的对象 </param>
        public virtual TEntity Add<TEntity>(TEntity entity, bool isSave = true) where TEntity : Entity
        {
            PublicHelper.CheckArgument(entity, "entity");
            //using (ObjContext)
            {
                ObjContext.AddObject(entity.GetType().Name, entity);
                if (isSave)
                {
                    this.SaveChanges();
                }
                return entity;
            }
        }

        /// <summary>
        ///     批量注册多个新的对象到仓储上下文中
        /// </summary>
        /// <typeparam name="TEntity"> 要注册的类型 </typeparam>
        /// <param name="entities"> 要注册的对象集合 </param>
        public virtual int Add<TEntity>(IEnumerable<TEntity> entities, bool isSave = true)
        {
            PublicHelper.CheckArgument(entities, "entities");

            //批量插入时 关闭自动检测和验证
            Context.Configuration.AutoDetectChangesEnabled = false;
            Context.Configuration.ValidateOnSaveEnabled = false;

            foreach (TEntity entity in entities)
            {
                ObjectStateEntry ose = null;
                EntityKey key = ObjContext.CreateEntityKey(entity.GetType().Name, entity);
                if (ObjContext.ObjectStateManager.TryGetObjectStateEntry(key, out ose))
                {
                    ObjContext.Detach(ose.Entity);
                }
                ObjContext.AddObject(entity.GetType().Name, entity);
            }

            return isSave ? this.SaveChanges() : 0;
        }

        #endregion

        #region UPDATE
        /// <summary>
        ///     注册一个更改的对象到仓储上下文中
        /// </summary>
        /// <typeparam name="TEntity"> 要注册的类型 </typeparam>
        /// <param name="entity"> 要注册的对象 </param>
        public virtual TEntity Update<TEntity>(TEntity entity, bool isSave = true) where TEntity : Entity
        {
            PublicHelper.CheckArgument(entity, "entity");

            try
            {
                var key = ObjContext.CreateEntityKey(entity.GetType().Name, entity);
                object obj;
                if (ObjContext.TryGetObjectByKey(key, out obj))
                {
                    ObjContext.ApplyCurrentValues<TEntity>(entity.GetType().Name, entity);
                }
                else
                {
                    ObjectStateEntry ose = null;
                    if (!ObjContext.ObjectStateManager.TryGetObjectStateEntry(key, out ose))
                    {
                        ObjContext.AddObject(entity.GetType().Name, entity);
                    }
                    else
                    {
                        ObjContext.AttachTo(entity.GetType().Name, entity);
                    }
                }
                if (isSave)
                {
                    this.SaveChanges();
                }
                return entity;
            }
            catch (Exception e)
            {
                ExceptionHand(e);
                throw;
                return entity;
            }
        }

        public virtual int Update<T>(IEnumerable<T> entitys, bool isSave = true) where T : Entity
        {
            PublicHelper.CheckArgument(entitys, "entitys");

            //批量插入时 关闭自动检测和验证
            Context.Configuration.AutoDetectChangesEnabled = false;
            Context.Configuration.ValidateOnSaveEnabled = false;
            Context.Database.CommandTimeout = 5 * 60;

            foreach (var entity in entitys)
            {
                this.Update(entity, false);
            }

            return isSave ? this.SaveChanges() : 0;
        }

        public virtual DbSet<TEntity> Update(DbSet<TEntity> dbset)
        {
            PublicHelper.CheckArgument(dbset, "dbset");
            Context.Set<TEntity>().AddRange(dbset);
            Context.SaveChanges();
            return dbset;
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public virtual int UpdateBySql<T>(Expression<Func<T, bool>> predicate, Expression<Func<T, T>> updater, bool isSave = true) where T : Entity
        {
            return Context.Update<T>(predicate, updater, isSave);
        }
        [MethodImpl(MethodImplOptions.Synchronized)]
        public virtual int UpdateBySql<T>(IEnumerable<Updaters<T>> updaters, bool isSave = true)
        where T : Entity
        {
            int result = 0;
            if (updaters != null && updaters.Count() > 0)
            {
                lock (lockUpdater)
                {
                    result = updaters.Select(c =>
                    {
                        return UpdateBySql<T>(c.Predicate, c.Updater, false);
                    }).Count();
                    if (result > 0)
                    {
                        result = isSave ? this.SaveDbComnds() : 0;
                    }
                }
            }
            return result;
        }
        public virtual int UpdateBatch<T>(IEnumerable<T> updaters, bool isSave = true)
            where T : Entity
        {
            IEnumerable<T> adders = updaters.Where(c => c.RowState == DataRowState.Added || c.RowState == 0);
            IEnumerable<T> updates = updaters.Where(c => c.RowState == DataRowState.Modified);
            IEnumerable<T> delers = updaters.Where(c => c.RowState == DataRowState.Added);

            if (adders != null && adders.Count() > 0)
            {
                this.Add(adders, false);
            }

            if (updates != null && updates.Count() > 0)
            {
                this.Update(updates, false);
            }

            if (delers != null && delers.Count() > 0)
            {
                this.Delete(delers, false);
            }

            return isSave ? this.SaveChanges() : 0;
        }
        #endregion

        #region DELETE
        public virtual int Delete<TEntity>(bool isSave = true, params object[] keyValues)
        {
            PublicHelper.CheckArgument(keyValues, "keyValues");
            return Delete(this.GetByID(keyValues), isSave);
        }
        /// <summary>
        ///   注册一个删除的对象到仓储上下文中
        /// </summary>
        /// <typeparam name="TEntity"> 要注册的类型 </typeparam>
        /// <param name="entity"> 要注册的对象 </param>
        public virtual int Delete<TEntity>(TEntity entity, bool isSave = true)
        {
            PublicHelper.CheckArgument(entity, "entity");
            //using (ObjContext)
            {
                ObjectStateEntry ose = null;
                object obj;
                EntityKey key = ObjContext.CreateEntityKey(entity.GetType().Name, entity);
                if (ObjContext.TryGetObjectByKey(key, out obj))
                {
                    ObjContext.DeleteObject(obj);
                }
                else if (ObjContext.ObjectStateManager.TryGetObjectStateEntry(key, out ose))
                {
                    ObjContext.ObjectStateManager.ChangeObjectState(ose, EntityState.Deleted);
                }

                return isSave ? this.SaveChanges() : 0;
            }
        }

        /// <summary>
        ///   批量注册多个删除的对象到仓储上下文中
        /// </summary>
        /// <typeparam name="TEntity"> 要注册的类型 </typeparam>
        /// <param name="entities"> 要注册的对象集合 </param>
        public virtual int Delete<TEntity>(IEnumerable<TEntity> entities, bool isSave = true)
        {
            PublicHelper.CheckArgument(entities, "entities");
            //using (ObjContext)
            {
                foreach (var entity in entities)
                {
                    //object ose = null;
                    //EntityKey key = ObjContext.CreateEntityKey(entity.GetType().Name, entity);
                    //if (ObjContext.TryGetObjectByKey(key, out ose))
                    //{
                    //    ObjContext.AttachTo(entity.GetType().Name, ose);
                    //    ObjContext.DeleteObject(ose);
                    //}
                    this.Delete<TEntity>(entity, false);

                }
                return isSave ? this.SaveChanges() : 0;
            }
        }

        /// <summary>
        ///     删除所有符合特定表达式的数据
        /// </summary>
        /// <param name="predicate"> 查询条件谓语表达式 </param>
        /// <param name="isSave"> 是否执行保存 </param>
        /// <returns> 操作影响的行数 </returns>
        public virtual int Delete<T>(Expression<Func<T, bool>> predicate) where T : Entity
        {
            PublicHelper.CheckArgument(predicate, "predicate");
            IEnumerable<T> entities = Context.Set<T>().Where(predicate);
            return Delete(entities);
        }

        /// <summary>
        /// 生成SQL删除
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public virtual int DeleteBySql<T>(Expression<Func<T, bool>> predicate) where T : Entity
        {
            return Context.Delete<T>(predicate);
        }
        #endregion

        #region Transaction
        /// <summary>
        /// 数据库实例连接
        /// </summary>
        protected DbConnection Conn { get { return ObjContext.Connection; } }

        /// <summary>
        /// 获取一个事务，先调用BeginTransaction，用完需调EndTransaction
        /// </summary>
        protected DbTransaction Trans { get; set; }

        /// <summary>
        /// 开启事务顺序：BeginTransaction->Commit->Rollback->EndTransaction
        /// </summary>
        /// <param name="isolationLevel"></param>
        /// <returns></returns>
        protected DbTransaction BeginTransaction(System.Data.IsolationLevel? isolationLevel = null)
        {
            if ((Conn.State == ConnectionState.Closed))
            {
                Conn.Open();
            }
            Trans = isolationLevel.HasValue ? Conn.BeginTransaction(isolationLevel.Value) : Conn.BeginTransaction();

            return Trans;
        }

        /// <summary>
        /// 提交事务
        /// </summary>
        protected void Commit()
        {
            Trans.Commit();
        }

        /// <summary>
        /// 事务回滚
        /// </summary>
        protected void Rollback()
        {
            Trans.Rollback();
        }

        /// <summary>
        /// 关闭事务
        /// </summary>
        protected void EndTransaction()
        {
            if (!(Conn.State == ConnectionState.Closed))
            {
                Conn.Close();
                //Conn.Dispose();
            }
        }
        #endregion

        public virtual void Execute(DelegateHelper delegater)
        {
            delegater.Execute(this.ObjContext);
        }

        public new void Dispose()
        {
            Context.Dispose();
            ObjContext.Dispose();

            if (!(Conn.State == ConnectionState.Closed))
            {
                Conn.Close();
                Conn.Dispose();
            }
        }

        #region 日志记录
        protected void ExceptionHand(Exception ex)
        {
            Loger.WriteLog(ex);
        }
        #endregion
    }

    public class Updaters<T>
    {
        public Expression<Func<T, bool>> Predicate { get; set; }
        public Expression<Func<T, T>> Updater { get; set; }

        public Updaters()
        {

        }
        public Updaters(Expression<Func<T, bool>> predicate, Expression<Func<T, T>> updater)
        {
            this.Predicate = predicate;
            this.Updater = updater;
        }
    }
}